package com.spzx.product.service.impl;

import com.spzx.common.core.utils.StringUtils;
import com.spzx.product.service.TestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
public class TestServiceImpl implements TestService {

   @Autowired
   private StringRedisTemplate stringRedisTemplate;
    
 /*  @Override
   public synchronized void testLock() {   //synchronized本地锁只能锁住同一工程内的资源，在分布式系统里面都存在局限性。此时需要分布式锁。
      // 查询Redis中的num值
      String value = (String)this.stringRedisTemplate.opsForValue().get("num");
      // 没有该值return
      if (StringUtils.isBlank(value)){
         return ;
      }
      // 有值就转成成int
      int num = Integer.parseInt(value);
      // 把Redis中的num值+1
      this.stringRedisTemplate.opsForValue().set("num", String.valueOf(++num));
   }*/


   /*分布式锁使用的逻辑如下：
   尝试获取锁
   成功：执行业务代码
           执行业务
			try{
      获取锁
      业务代码-宕机
   } catch(){

   }finally{
      释放锁
   }
   失败：等待；*/
   /**
    * 采用SpringDataRedis实现分布式锁
    * 原理：执行业务方法前先尝试获取锁（setnx存入key val），如果获取锁成功再执行业务代码，业务执行完毕后将锁释放(del key)
    */
  /* @Override
   public void testLock() {
      //0.先尝试获取锁 setnx key val
      //Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent("lock", "lock");
      Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent("lock", "lock", 3, TimeUnit.SECONDS);  //设置锁过期时间,但是会导致释放其他服务器的锁
      if (flag){
         //int i=1/0;   异常或服务器宕机会导致无法释放锁,造成死锁,需要设置锁过期时间解决.
         //获取锁成功，执行业务代码
         //1.先从redis中通过key num获取值  key提前手动设置 num 初始值：0
         String value = stringRedisTemplate.opsForValue().get("num");
         //2.如果值为空则非法直接返回即可
         if (StringUtils.isBlank(value)){
            return;
         }
         //3.对num值进行自增加一
         int num = Integer.parseInt(value);
         stringRedisTemplate.opsForValue().set("num",String.valueOf(++num));

         stringRedisTemplate.delete("lock");

      }else {
         try {
            Thread.sleep(100);
            this.testLock();
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
      }
   }*/

  /* @Override
   public void testLock() {
      //0.先尝试获取锁 setnx key val
      String uuid = UUID.randomUUID().toString();
      Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent("lock", "lock", 3, TimeUnit.SECONDS);  //设置锁过期时间,但是会导致释放其他服务器的锁
      if (flag){
         //int i=1/0;   异常或服务器宕机会导致无法释放锁,造成死锁,需要设置锁过期时间解决.
         //获取锁成功，执行业务代码
         //1.先从redis中通过key num获取值  key提前手动设置 num 初始值：0
         String value = stringRedisTemplate.opsForValue().get("num");
         //2.如果值为空则非法直接返回即可
         if (StringUtils.isBlank(value)){
            return;
         }
         //3.对num值进行自增加一
         int num = Integer.parseInt(value);
         stringRedisTemplate.opsForValue().set("num",String.valueOf(++num));

         //释放锁之前先判断锁是否属于自己的锁,但是会导致删除操作缺乏原子性
         if (uuid.equals((String)stringRedisTemplate.opsForValue().get("lock"))){
            stringRedisTemplate.delete("lock");
         }
      }else {
         try {
            Thread.sleep(100);
            this.testLock();
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }*/


   
   //使用LUA脚本优化分布式锁
   /**
    * 采用SpringDataRedis实现分布式锁
    * 原理：执行业务方法前先尝试获取锁（setnx存入key val），如果获取锁成功再执行业务代码，业务执行完毕后将锁释放(del key)
    */
   /*@Override
   public void testLock() {
      //0.先尝试获取锁 setnx key val
      String uuid = UUID.randomUUID().toString();
      Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent("lock", "lock", 3, TimeUnit.SECONDS);  //设置锁过期时间,但是会导致释放其他服务器的锁
      if (flag){
         //int i=1/0;   异常或服务器宕机会导致无法释放锁,造成死锁,需要设置锁过期时间解决.
         //获取锁成功，执行业务代码
         //1.先从redis中通过key num获取值  key提前手动设置 num 初始值：0
         String value = stringRedisTemplate.opsForValue().get("num");
         //2.如果值为空则非法直接返回即可
         if (StringUtils.isBlank(value)){
            return;
         }
         //3.对num值进行自增加一
         int num = Integer.parseInt(value);
         stringRedisTemplate.opsForValue().set("num",String.valueOf(++num));

         //先创建脚本对象 DefaultRedisScript泛型脚本语言返回值类型 Long 0：失败 1：成功
         DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();

         //设置脚本
         String script="if redis.call(\"get\",KEYS[1] == ARGV[1]\n)"+
                 "then\n"+
                 " return redis.call(\"del\",KEYS[1])\n"+
                 "else\n"+
                 " return 0\n"+
                 "end";
         redisScript.setScriptText(script);
         //设置响应类型
         redisScript.setResultType(Long.class);
stringRedisTemplate.execute(redisScript, Arrays.asList("lock"),uuid);
      }else {
         try {
            Thread.sleep(100);
            this.testLock();
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }*/


   @Override
   public void testLock() {
      
   }
}